using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using IdentityServer4.Extensions;
using IdentityServer4.Models;
using IdentityServer4.Services;
using IdentityServer4.Test;
using Idp_Login.Service;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;

namespace Idp_Login
{
    //2.实现 IProfileService 以添加自定义 claim，
    //这两步共同完成的是 “使用已有用户数据自定义Claim”，并不是自定义 Claim 就非得把两个都实现。
    //IProfileService 并不是必须的，
    //它的用处在于 可以根据 context.RequestedClaimTypes.Any() 的具体项从数据库拿只需要的字段（拼sql啥的）。我是这么理解的不知道对不对。

    public class CustomProfileService : IProfileService
    {
        protected readonly ILogger Logger;


        private IAdminService _adminService;

        public CustomProfileService(IAdminService adminService, ILogger<CustomProfileService> logger)
        {
            _adminService = adminService;
            Logger = logger;
        }

        /// <summary>
        /// 每当请求有关用户的 身份信息单元（例如，在令牌创建期间或通过userinfo端点），都会调用此方法
        /// access_token 和 id_token 的生成都会调用进来
        /// 
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        public virtual Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
            context.LogProfileRequest(Logger);

            // 如果要做到特定的 access_token / id_token 中出现, 可以从redis 中获取

            //注: ApiResource 的第三个参数 claimTypes 为null 或 count ==0 时, context.RequestedClaimTypes.Any() 为 false
            if (context.RequestedClaimTypes.Any() /*是否有请求Claim信息*/
                && context.IssuedClaims.Count == 0  /*没有claims*/
                && context.Subject.Claims.Count() > 0  /*有唯一标识*/ )
            {

                #region 补齐所需的claims
                //var needClaims = context.RequestedClaimTypes;
                //var haveClaims = context.Subject.Claims.ToList();
                //var lackClaims = needClaims.Except(haveClaims.Select(a => a.Type)); //差集 needClaims独有的 

                //foreach (var item in lackClaims)
                //{
                //    haveClaims.Add(new Claim(item, ""));
                //}

                //context.IssuedClaims = haveClaims;
                #endregion

                //这边的claims会写入对应的Caller  (可以理解成 claims会写入 access_Token / id_token )
                if (context.Caller == "ClaimsProviderAccessToken")
                {
                    context.IssuedClaims = context.Subject.Claims.ToList();

                    //不要用下面的方式添加自定义的, 添加不进去. 不自动为什么, 
                    //context.IssuedClaims.AddRange(claims);
                }
                else if (context.Caller == "ClaimsProviderIdentityToken")
                {
                    //如果Claims 要写入id_Token 需要设置client 的   AlwaysIncludeUserClaimsInIdToken = true, 

                    var claims = context.Subject.Claims.ToList();

                    //根据业务自己来
                    if (context.RequestedClaimTypes.FirstOrDefault() == "role")
                    {
                        var userid = context.Subject.GetSubjectId();//根据用户唯一标识查找用户信息

                        var user = _adminService.GetById(Convert.ToInt32(userid)).Result;

                        if (user != null)
                        {
                            //根据业务,根据用户获取 claims
                            claims.Add(new Claim("Remark", user.Remark));//测试用

                            claims.AddRange(new List<Claim>()
                                    {
                                        //自定义的
                                        new Claim("phonemodel", "huawei"),
                                        new Claim("phoneprise", "5000元"),
                                    });

                            context.IssuedClaims = claims;

                            //不要用下面的方式添加自定义的, 添加不进去. 不自动为什么, 
                            //context.IssuedClaims.AddRange(claims);


                            #region 下面2种方式会过滤, 不知道怎么用.不会用

                            //claims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToList();
                            //context.IssuedClaims = claims;

                            //和上面的效果是一样的 只会添加第一条Claims(内部会进行过滤，只将用户请求的Claim加入到 context.IssuedClaims 集合中 这样我们的请求方便能正常获取到所需Claim)  , 
                            //context.AddRequestedClaims(claims); 

                            #endregion

                        }
                    }
                }
            }
            context.LogIssuedClaims(Logger);

            return Task.CompletedTask;
        }


        /// <summary>
        /// 验证用户是否有效 例如：token创建或者验证,或者注销
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        public virtual Task IsActiveAsync(IsActiveContext context)
        {
            Logger.LogDebug("IsActive called from: {caller}", context.Caller);
            //get subject from context (set in ResourceOwnerPasswordValidator.ValidateAsync),
            //var userId = context.Subject.Claims.FirstOrDefault(x => x.Type == "user_id");

            //var userid = context.Subject.GetSubjectId();
            //context.IsActive = true;//默认就是true

            //var user = _adminService.GetById(Convert.ToInt32(userid)).Result;
            //if (user.Remark == "禁用")
            //{
            //根据具体的业务逻辑来
            //    //context.IsActive = false;
            //    //context.IsActive = user?.IsActive == true;
            //}

            return Task.CompletedTask;
        }

    }
}
